package sgsi.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;

import sgsi.controller.dao.ArticuloDao;
import sgsi.controller.dao.PaletDao;
import sgsi.controller.dao.TipoDeArticuloDao;
import sgsi.model.comandos.BuscarArticuloComando;
import sgsi.model.comandos.NuevoArticuloComando;
import sgsi.model.entidades.Articulo;
import sgsi.model.entidades.Palet;
import sgsi.model.entidades.TipoDeArticulo;
import sgsi.view.json.VistaArticuloJson;

/**
 * Clase controladora creada para atender acciones relativas a la gestion de
 * Art&iacute;culos.
 * 
 *@See SgsiMultiActionController
 * 
 */
public class GestionarArticulos extends SgsiMultiActionController {

	private ArticuloDao articuloDao;
	private TipoDeArticuloDao tipoDeArticuloDao;
	private PaletDao paletDao;

	/**
	 * Acci&oacute:n para el seteo de atributos iniciales para la pantalla de
	 * nueva/ edici&oacute:n de un art&iacute;culo.
	 * <p>
	 * Crea una nueva instacia del comando <code>NuevoArticuloComando</code> y
	 * la asocia a la pantalla.
	 * 
	 * @param req
	 *            Objeto HttpServletRequest que modela la peticion por parte de
	 *            la capa de presentaci&oacute;n.
	 * @param resp
	 *            Objeto HttpServletResponse que modela la respuesta del
	 *            servidor.
	 * @return ModelAndView Objecto que modela la Vista a retornar para la capa
	 *         de presentacion: en este caso particular es la pantalla de
	 *         <code>nuevoArticulo.html</code>.
	 * 
	 * @see NuevoArticuloComando
	 */
	public ModelAndView cargaInicialNuevo(HttpServletRequest req, HttpServletResponse resp) {
		NuevoArticuloComando nuevoArticuloComando = new NuevoArticuloComando();
		nuevoArticuloComando.setTiposDeArticulos(tipoDeArticuloDao.obtenerTodos());
		return new ModelAndView("nuevoArticulo.html", "command", nuevoArticuloComando);
	}

	/**
	 * Accion para el seteo de atributos iniciales para la busqueda de
	 * art&iacute;culos.
	 * <p>
	 * Crea una nueva instacia del comando <code>BuscarArticuloComando</code> y
	 * la asocia a la pantalla.
	 * 
	 * @param req
	 *            Objeto HttpServletRequest que modela la peticion por parte de
	 *            la capa de presentaci&oacute;n.
	 * @param resp
	 *            Objeto HttpServletResponse que modela la respuesta del
	 *            servidor.
	 * 
	 * @return ModelAndView Objecto que modela la Vista a retornar para la capa
	 *         de presentacion: en este caso particular es la pantalla de
	 *         <code>actualizarArea.html</code>.
	 * 
	 * @see BuscarArticuloComando
	 */
	public ModelAndView cargaInicialActualizar(HttpServletRequest req, HttpServletResponse resp) {
		BuscarArticuloComando buscarArticuloComando = new BuscarArticuloComando();
		buscarArticuloComando.setTiposDeArticulos(tipoDeArticuloDao.obtenerTodos());
		return new ModelAndView("actualizarArticulo.html", "command", buscarArticuloComando);
	}

	private ModelAndView presentarErroresEnLaValidacion(HttpServletRequest req, NuevoArticuloComando comando) {
		req.setAttribute("errors", getErrors().getAllErrors());

		comando.setTiposDeArticulos(tipoDeArticuloDao.obtenerTodos());

		return new ModelAndView("nuevoArticulo.html", "command", comando);
	}

	/**
	 * Acci&oacute:n para la creacion de un nuevo art&iacute;culo.
	 * <p>
	 * Recupera las propiedades de la nueva intancia desde el comando
	 * <code>NuevoArticuloComando</code> y lo guarda en la base de datos.
	 * 
	 * @param req
	 *            Objeto HttpServletRequest que modela la peticion por parte de
	 *            la capa de presentaci&oacute;n.
	 * @param resp
	 *            Objeto HttpServletResponse que modela la respuesta del
	 *            servidor.
	 * @return ModelAndView Objecto que modela la Vista a retornar para la capa
	 *         de presentacion: si la creacion fue exitosa retorna a la pantalla
	 *         principal <code>home.html</code>, en otro caso, retorna a la
	 *         misma pantalla de dodne fue invocada, retornado el/los mensaje/s
	 *         de error.
	 * 
	 * @see NuevoArticuloComando
	 */
	public ModelAndView nuevoArticulo(HttpServletRequest req, HttpServletResponse resp,
			NuevoArticuloComando nuevoArticuloComando) {

		if (getErrors().hasErrors()) {
			return presentarErroresEnLaValidacion(req, nuevoArticuloComando);
		}

		if (validarModificacion(nuevoArticuloComando)) {
			return presentarErroresEnLaValidacion(req, nuevoArticuloComando);
		}

		TipoDeArticulo tipoDeArticulo = tipoDeArticuloDao.obtenerPorCodigo(nuevoArticuloComando
				.getCodigoTipoDeArticulo());

		Articulo articulo = new Articulo();
		articulo.setCodigoArticulo(nuevoArticuloComando.getCodigoArticulo());
		articulo.setCodigoDeBarras(nuevoArticuloComando.getCodigoDeBarras());
		articulo.setNombre(nuevoArticuloComando.getNombre());
		articulo.setTipoDeArticulo(tipoDeArticulo);

		articuloDao.guardar(articulo);
		return new ModelAndView("home.html");
	}

	private boolean validarModificacion(NuevoArticuloComando nuevoArticuloComando) {

		Articulo articulo = articuloDao.obtenerPorCodigo(nuevoArticuloComando.getCodigoArticulo());
		if (articulo == null)
			return false;

		if (paletDao.obtenerPorArticulo(articulo.getCodigoArticulo()).isEmpty())
			return false;

		if (nuevoArticuloComando.getCodigoTipoDeArticulo() != articulo.getTipoDeArticulo().getCodigoTipoDeArticulo()) {
			getErrors().addError(
					new ObjectError("ArticuloModificacionDeTipo", null, null, StringEscapeUtils
							.escapeHtml("No se puede modificar el Tipo de articulo ya que hay Palets alocados.")));
			return true;
		}

		return false;
	}

	/**
	 * Acci&oacute;n para la busqueda de art&iacute;culos.
	 * <p>
	 * Recupera los criterios de busqueda, y los aplica en una busqueda
	 * seg&uacute;n c&oacute;digo , tipo o c&oacute;digo de barras. Retorna una
	 * lista de los art&iacute;culos que coinciden con los criterios de busqueda
	 * asyncorincamente, via un mensaje serializado seg&uacute;n estandar JSON.
	 * 
	 * @param req
	 *            Objeto HttpServletRequest que modela la peticion por parte de
	 *            la capa de presentaci&oacute;n.
	 * @param resp
	 *            Objeto HttpServletResponse que modela la respuesta del
	 *            servidor.
	 * @throws IOException
	 *             Si es que hay un error en la serializac&iacute;n de la
	 *             respuesta
	 */
	public void buscarArticulos(HttpServletRequest req, HttpServletResponse resp) throws IOException {

		int codigo;
		int tipoSeleccionado = 0;
		int codigoDeBarras = 0;
		String nombre = req.getParameter("nombre");
		List<Articulo> articulos;

		if (StringUtils.isNotBlank(req.getParameter("codigoArticulo"))) {
			codigo = Integer.parseInt(req.getParameter("codigoArticulo"));
			articulos = new ArrayList<Articulo>();
			Articulo articulo = articuloDao.obtenerPorCodigo(codigo);
			if (articulo != null) {
				articulos.add(articulo);
			}
		} else {
			if (StringUtils.isNotBlank(req.getParameter("codigoDeBarras"))) {
				codigoDeBarras = Integer.parseInt(req.getParameter("codigoDeBarras"));
			}
			if (StringUtils.isNotBlank(req.getParameter("tipoDeArticuloSeleccionado"))) {
				tipoSeleccionado = Integer.parseInt(req.getParameter("tipoDeArticuloSeleccionado"));
			}
			articulos = articuloDao.obtenerTodosBusqueda(nombre, tipoSeleccionado, codigoDeBarras);
		}

		JSONObject jsonObj = new JSONObject();
		jsonObj.accumulate("articulos", serializar(articulos));
		PrintWriter writer = resp.getWriter();
		writer.append(jsonObj.toString());

	}

	private List<VistaArticuloJson> serializar(List<Articulo> articulos) {
		List<VistaArticuloJson> clientesSerializados = new ArrayList<VistaArticuloJson>();
		for (Articulo articulo : articulos) {
			clientesSerializados.add(new VistaArticuloJson(articulo));
		}
		return clientesSerializados;
	}

	/**
	 * Acci&oacute:n para la eliminaci&oacute;n l&oacute;gica de un art&iacute;culo.
	 * <p>
	 * Recupera el c&oacute:digo a fin de obtener la intancia asociada desde la
	 * base da datos y la eliminar logicamente, persistendo el cambio en la base
	 * de datos.
	 * <p>
	 * Informa asyncorincamente el resultado de la eliminaci&oacute;n, via un
	 * mensaje serializado seg&uacute;n estandar JSON.
	 * 
	 * @param req
	 *            Objeto HttpServletRequest que modela la peticion por parte de
	 *            la capa de presentaci&oacute;n.
	 * @param resp
	 *            Objeto HttpServletResponse que modela la respuesta del
	 *            servidor.
	 * @throws IOException
	 *             Si es que hay un error en la serializac&iacute;n de la
	 *             respuesta
	 */
	public void eliminar(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		int codigo = Integer.parseInt(req.getParameter("codigo"));

		Articulo articulo = articuloDao.obtenerPorCodigo(codigo);
		articulo.marcarComoBorrada();

		articuloDao.guardar(articulo);

		JSONObject jsonObj = new JSONObject();
		jsonObj.accumulate("exito", true);
		PrintWriter writer = resp.getWriter();
		writer.append(jsonObj.toString());
	}

	/**
	 * Determina a partir del c&oacute;digo de un art&iacute;culo si puede o no
	 * ser borrada.
	 * 
	 * @param req
	 *            Objeto HttpServletRequest que modela la peticion por parte de
	 *            la capa de presentaci&oacute;n.
	 * @param resp
	 *            Objeto HttpServletResponse que modela la respuesta del
	 *            servidor.
	 * 
	 * @throws IOException
	 *             Si es que hay un error en la serializac&iacute;n de la
	 *             respuesta
	 */
	public void puedoBorrarArticulo(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		int codigoArticulo = Integer.parseInt(req.getParameter("codigo"));

		List<Palet> palets = paletDao.obtenerPorArticulo(codigoArticulo);

		JSONObject jsonObj = new JSONObject();
		jsonObj.accumulate("puedeSerBorrada", palets.isEmpty());
		PrintWriter writer = resp.getWriter();
		writer.append(jsonObj.toString());
	}

	/**
	 * Acci&oacute:n para la modificacion de art&iacute;culos.
	 * <p>
	 * Recupera el c&oacute;digo y apartir de este recupera la instancia de la
	 * misma, y popula el comando <code>NuevoArticuloComando</code> con sus
	 * datos.
	 * 
	 * @param req
	 *            Objeto HttpServletRequest que modela la peticion por parte de
	 *            la capa de presentaci&oacute;n.
	 * @param resp
	 *            Objeto HttpServletResponse que modela la respuesta del
	 *            servidor.
	 * @param BuscarArticuloComando
	 *            Comando con los datos ingresados en para la modificacion.
	 * 
	 * @return ModelAndView Objecto que modela la Vista a retornar para la capa
	 *         de presentacion: en este caso particular es la pantalla de
	 *         <code>nuevaArea.html</code>.
	 */
	public ModelAndView modificar(HttpServletRequest req, HttpServletResponse resp) {

		BuscarArticuloComando buscarArticuloComando = obtenerComando(req);
		Articulo articulo = articuloDao.obtenerPorCodigo(buscarArticuloComando.getCodigoAOperar());

		NuevoArticuloComando nuevo = new NuevoArticuloComando();
		nuevo.setCodigoArticulo(articulo.getCodigoArticulo());
		nuevo.setCodigoDeBarras(String.valueOf(articulo.getCodigoDeBarras()));
		nuevo.setNombre(articulo.getNombre());
		nuevo.setTiposDeArticulos(tipoDeArticuloDao.obtenerTodos());
		nuevo.setCodigoTipoDeArticulo(articulo.getTipoDeArticulo().getCodigoTipoDeArticulo());

		return new ModelAndView("nuevoArticulo.html", "command", nuevo);
	}
	
	private BuscarArticuloComando obtenerComando(HttpServletRequest req) {
		BuscarArticuloComando buscarArticuloComando = new BuscarArticuloComando();
		ServletRequestDataBinder binder = new ServletRequestDataBinder(
				buscarArticuloComando);
		binder.bind(req);
		return buscarArticuloComando;
	}

	public void setArticuloDao(ArticuloDao articuloDao) {
		this.articuloDao = articuloDao;
	}

	public void setTipoDeArticuloDao(TipoDeArticuloDao tipoDeArticuloDao) {
		this.tipoDeArticuloDao = tipoDeArticuloDao;
	}

	public void setPaletDao(PaletDao paletDao) {
		this.paletDao = paletDao;
	}
}
